package com.shenhaofeng.app.adapter;

import android.os.Bundle;
import android.support.v7.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;


/**
 * RecyclerView的适配器,某些时候需要分模块添加数据,这个适配器提供了模块化的添加数据.
 * 主要适用于分块数据的数据列表，例如视频分区数据，每个module只有一种布局，所以如果需要交错使用不同布局需要在
 * {@link ModuleAdapter#onBindViewHolder(RecyclerView.ViewHolder, int)}中通过数据来控制view的显示状态
 */
public abstract class ModuleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    //维护的Item管理器，用于管理module中的item
    private ModuleManager moduleManager;


    /**
     * 这是一个快捷方法，添加一个module到{@link ModuleManager}中，这一步是必须的，
     * 并且必须在Module添加数据之前，否则module无法添加数据,并且将会抛出一个异常。
     *
     * @param module 需要添加的Module
     */
    public void addModule(Module module) {
        module.setAdapter(this);
        getModuleManager().addModule(module);
    }


    /**
     * 添加Module
     *
     * @param index
     * @param module
     */
    public void addModule(int index, Module module) {
        module.setAdapter(this);
        getModuleManager().addModule(module, index);
    }


    /**
     * 这是一个快捷方法，根据item的position找到对应的Module
     *
     * @param position item在Adapter中的位置
     * @param <T>      Module中的数据类型
     * @return 对应位置的Module
     */
    public <T> Module<T> findModuleByPosition(int position) {
        return getModuleManager().findModuleFromPosition(position);
    }


    /**
     * 这是一个快捷方法，根据Item的position找到对应的Item
     *
     * @param position item在适配器中的位置
     * @param <T>      Module中的数据类型
     * @return 在指定位置的Item实例
     */
    public <T> T findItemByPostion(int position) {
        Module<T> module = findModuleByPosition(position);
        return module.getItemFromAdapterPosition(position);
    }


    /**
     * 这是一个快捷方法，从{@link ModuleManager}中移除一个Module，这个操作同时会移除Module相对应的item。
     *
     * @param module 需要移除的Module
     */
    public void removeModule(Module module) {
        getModuleManager().removeModule(module);
    }


    /**
     * 这是一个快捷方法，清空所有在{@link ModuleManager}中的Module，需要注意的是，
     * 如果有些固定的Module你不想移除请不要调用该方法，最好的方式是重写禁用该方法并定义一个新方法老移除所有可清除的Module
     */
    public void clearAllItem() {
        List<Module<?>> modules = getModuleManager().getModules();
        for (int i = 0; i < modules.size(); i++) {
            modules.get(i).clear(true);
        }
    }


    @Override
    public int getItemCount() {
        return getModuleManager().getItemCount();
    }


    @Override
    public int getItemViewType(int position) {
        Module module = getModuleManager().findModuleFromPosition(position);
        return module.getType();
    }


    /**
     * 通过该方法来获取维护的ItemManger的实例，不要轻易调用这个方法,因为某些Module可能是有特殊作用，扇子操作肯呢个导致不可知的后果，慎用。
     *
     * @return 当前适配器中ModuleManager的唯一实例
     */
    public ModuleManager getModuleManager() {
        if (moduleManager == null) {
            moduleManager = new ModuleManager(this);
        }
        return moduleManager;
    }


    /**
     * 这个类的作用是管理所有的Module，并提供一些全局的Module的快捷方法，这是ModuleAdapter的核心类，涉及到Module的一些操作
     * ，一般情况下都是Adapter封装它的方法，保证不出现不可预知的错误
     */
    public static class ModuleManager {


        //适配器实例
        private ModuleAdapter adapter;

        //存放了所有模型组
        private List<Module<?>> modules = new ArrayList<>();


        /**
         * 默认构造烦烦噶
         *
         * @param adapter 适配器实例
         */
        protected ModuleManager(ModuleAdapter adapter) {
            this.adapter = adapter;
        }


        /**
         * 通过position找到对应的module
         *
         * @param position item在适配器中的位置
         * @return 在适配器中指定位置的Module
         */
        public <M> Module<M> findModuleFromPosition(int position) {
            List<Module<?>> modules = getModules();
            int count = 0;
            for (int i = 0; i < modules.size(); i++) {
                count += modules.get(i).getCount();
                if (count > position) {
                    return (Module<M>) modules.get(i);
                }
            }
            return null;
        }


        /**
         * 取出所有的module
         *
         * @return 所有的Module
         */
        public List<Module<?>> getModules() {
            return modules;
        }


        /**
         * 添加一个新的Module
         *
         * @param module 待添加的Module
         * @param <T>    Module的item类型
         * @return
         */
        public <T> Module<T> addModule(Module<T> module) {
            modules.add(module);
            return module;
        }


        /**
         * 移除指定的module
         *
         * @param module 待移除的Module
         */
        public void removeModule(Module module) {
            if (modules.contains(module)) {
                int start = getModuleCountBefore(module);
                int count = module.getCount();
                modules.remove(module);
                adapter.notifyItemRangeRemoved(start, count);
            }

        }


        /**
         * 清空所有Module
         */
        public void clearModule() {
            int count = getItemCount();
            modules.clear();
            adapter.notifyItemRangeRemoved(0, count);
        }


        /**
         * 添加一个Module到指定的位置
         *
         * @param module 待添加的Module
         * @param index  Module添加的位置
         * @param <T>    Module的item类型
         * @return
         */
        public <T> Module<T> addModule(Module<T> module, int index) {
            modules.add(index, module);
            return module;
        }


        /**
         * 获取module的总数量
         *
         * @return Module的数量
         */
        public int getModuleCount() {
            return modules.size();
        }


        /**
         * 取出module的Item列表
         *
         * @param module 需要操作的Module
         * @param <T>    Module的item类型
         * @return Module中的所有Item列表
         */
        public <T> List<T> getModulItems(Module<T> module) {
            return module.getItemList();
        }


        /**
         * 获取在某个module之前的item数量
         *
         * @param module 指定过的Module
         * @return 指定Module之前的所有item数量
         */
        public int getModuleCountBefore(Module module) {
            int index = getModules().indexOf(module);
            if (index == -1) {
                return -1;
            }
            int size = 0;
            for (int i = 0; i < index; i++) {
                size += getModules().get(i).getCount();
            }
            return size;
        }


        /**
         * 获取item数量
         *
         * @return item的总数量
         */
        public int getItemCount() {
            int size = modules.size();
            int count = 0;
            for (int i = 0; i < size; i++) {
                Module<?> module = modules.get(i);
                List<?> items = module.getItemList();
                count += items.size();
            }
            return count;
        }

    }


    /**
     * ModuleAdapter将item拆分成了一个个单独的Module，每个Module都能操作在它职责范围内的数据，添加移除更新item
     *
     * @param <T>
     */
    public static final class Module<T> {

        //默认的Module类型
        public static final int NO_TYPE = 0;

        //item列表
        private List<T> itemList = new ArrayList<>();

        //保存的适配器引用
        private ModuleAdapter adapter;

        //Module类型
        private int type;

        //这里保存了额外的数据,可以保存一些状态等额外属性
        private Bundle data;


        /**
         * @param type
         * @param data
         */
        protected Module(int type, Bundle data) {
            this.type = type;
            this.data = data == null ? new Bundle() : new Bundle(data);
        }


        /**
         *
         */
        protected Module() {
            this(NO_TYPE, null);
        }


        /**
         * 创建指定类型的Module
         *
         * @param <T>
         * @return
         */
        public static <T> Module<T> createModule(int type) {
            return new Module<>(type, null);
        }


        /**
         * 创建指定类型的Module
         *
         * @param type module类型
         * @param data 额外数据
         * @param <T>
         * @return
         */
        public static <T> Module<T> createModule(int type, Bundle data) {
            return new Module<>(type, data);
        }


        /**
         * 获取Module的类型
         *
         * @return
         */
        public int getType() {
            return type;
        }


        /**
         * 这个方法在Module添加到适配器中后将会调用
         *
         * @param adapter
         */
        protected final void setAdapter(ModuleAdapter adapter) {
            this.adapter = adapter;
        }


//        /**
//         * 在某些必要的情况下，可以提供module
//         *
//         * @param adapter
//         */
//        public void bindAdapter(ModuleAdapter adapter) {
//            setAdapter(adapter);
//        }


        /**
         * 获取item,返回所有在这个Module中的数据
         *
         * @return
         */
        public List<T> getItemList() {
            return itemList;
        }


        /**
         * module的Item数量
         *
         * @return
         */
        public int getCount() {
            return itemList.size();
        }


        /**
         * 找到指定位置在适配器中的位置
         *
         * @param index 在这个Module中的位置
         * @return 在适配器中的位置
         */
        public int findAdapterPositionFromModuleIndex(int index) {
            guaranteeAdapter();
            int count = adapter.getModuleManager().getModuleCountBefore(this);
            return count + index;
        }


        /**
         * 根据适配器中的位置取出module的index
         *
         * @param position 在适配器中的位置
         * @return 在这个Module中的位置
         */
        public int findModuleIndexFromAdapterPosition(int position) {
            guaranteeAdapter();
            return position - adapter.getModuleManager().getModuleCountBefore(this);
        }


        /**
         * 通知某个item更新
         *
         * @param item
         */
        public void notifyItemChange(T item) {
            int position = findItemPosition(item);
            adapter.notifyItemChanged(position);
        }


        /**
         * 通知所有item更新
         */
        public void notifyChange() {
            if (getCount() > 0) {
                int start = findAdapterPositionFromModuleIndex(0);
                int end = findAdapterPositionFromModuleIndex(getCount() - 1);
                adapter.notifyItemRangeChanged(start, end);
            }
        }


        /**
         * 通过item实例获取在module中的index
         *
         * @param item
         * @return
         */
        public int findItemIndex(T item) {
            return itemList.indexOf(item);
        }


        /**
         * 通过item实例获取在adapter中的index
         *
         * @param item
         * @return
         */
        public int findItemPosition(T item) {
            return findAdapterPositionFromModuleIndex(itemList.indexOf(item));
        }


        /**
         * 这个方法用于确保已添加到适配器中
         */
        private void guaranteeAdapter() {
            if (adapter == null) {
                throw new IllegalStateException("必须将Module添加到适配器");
            }
        }


        /**
         * 添加新的数据到Module中
         *
         * @param item   新的数据
         * @param notify 是否通知适配器更新
         * @return 在Module中的位置
         */
        private synchronized int addItem(T item, boolean notify) {
            int index = itemList.size();
            itemList.add(item);
            int position = findAdapterPositionFromModuleIndex(index);
            if (notify) {
                adapter.notifyItemInserted(position);
            }
            return index;
        }


        /**
         * 添加新的数据到Module中
         *
         * @param index  在Module中的位置
         * @param item   新的数据
         * @param notify 是否通知适配器更新
         */
        private synchronized void addItem(int index, T item, boolean notify) {
            itemList.add(index, item);
            if (notify) {
                adapter.notifyItemInserted(findAdapterPositionFromModuleIndex(index));
            }
        }


        /**
         * 添加一组数据到Module中
         *
         * @param itemList 需要添加的数据
         * @param notify   是否通知更新数据
         */
        private synchronized void addItems(List<T> itemList, boolean notify) {
            int start = this.itemList.size();
            this.itemList.addAll(itemList);
            if (notify) {
                start = findAdapterPositionFromModuleIndex(start);
                adapter.notifyItemRangeInserted(start, itemList.size());
            }
        }


        /**
         * 添加一组数据到Module中
         *
         * @param index    在Module中的位置
         * @param itemList 需要添加的数据
         * @param notify   是否通知更新数据
         */
        private synchronized void addItems(int index, List<T> itemList, boolean notify) {
            int start = index;
            this.itemList.addAll(index, itemList);
            if (notify) {
                start = findAdapterPositionFromModuleIndex(start);
                adapter.notifyItemRangeInserted(start, itemList.size());
            }
        }


        /**
         * 快速添加一组数据到Module中，并通知观察者
         *
         * @param itemList
         */
        public void addItems(List<T> itemList) {
            addItems(itemList, true);
        }


        /**
         * 插入一组item
         *
         * @param index
         * @param itemList
         */
        public void addItems(int index, List<T> itemList) {
            addItems(index, itemList, true);
        }


        /**
         * 插入数据并通知适配器更新
         *
         * @param item
         * @return
         */
        public int addItem(T item) {
            return addItem(item, true);
        }


        /**
         * 插入数据到指定位置
         *
         * @param item
         * @param index
         */
        public void addItem(T item, int index) {
            addItem(index, item, true);
        }


        /**
         * 根据在Module中的位置来移除item并通知观察者
         *
         * @param index  在Module中的位置
         * @param notify 是否通知
         * @return 移除的Item
         */
        public synchronized T removeItemFromIndex(int index, boolean notify) {
            T item = itemList.remove(index);
            if (notify) {
                adapter.notifyItemRemoved(findAdapterPositionFromModuleIndex(index));
            }
            return item;
        }


        /**
         * 根据在Module中的位置来移除item
         *
         * @param index 在Module中的位置
         * @return 移除的Item
         */
        public T removeItemFromIndex(int index) {
            return removeItemFromIndex(index, true);
        }


        /**
         * 根据适配器中的位置移除item并通知观察者
         *
         * @param position 在适配器中的位置
         * @param notify   是否通知
         * @return 移除的Item
         */
        public synchronized T removeItemFromAdapterPosition(int position, boolean notify) {
            int index = findModuleIndexFromAdapterPosition(position);
            T item = itemList.remove(index);
            if (notify) {
                adapter.notifyItemRemoved(position);
            }
            return item;
        }


        /**
         * 根据适配器中的位置移除item
         *
         * @param position 在适配器中的位置
         * @return
         */
        public T removeItemFromAdapterPosition(int position) {
            return removeItemFromAdapterPosition(position, true);
        }


        /**
         * 根据在Module中的位置获取Item
         *
         * @param index
         * @return
         */
        public T getItemFormIndex(int index) {
            return itemList.get(index);
        }


        /**
         * 根据在适配器中的位置获取Item
         *
         * @param position
         * @return
         */
        public T getItemFromAdapterPosition(int position) {
            int count = adapter.getModuleManager().getModuleCountBefore(this);
            return getItemFormIndex(position - count);
        }


        /**
         * 清除数据
         *
         * @param notify
         */
        private void clear(boolean notify) {
            int start = adapter.getModuleManager().getModuleCountBefore(this);
            int end = getCount();
            getItemList().clear();
            if (notify) {
                adapter.notifyItemRangeRemoved(start, end);
            }
        }


        /**
         * 清除Module中的所有Item数据
         */
        public void clear() {
            clear(true);
        }


    }

}
